package com.sc.nft.controller;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.sc.nft.annotation.UserLogin;
import com.sc.nft.annotation.WithoutSign;
import com.sc.nft.controller.request.IdRequest;
import com.sc.nft.controller.request.WalletPayRequest;
import com.sc.nft.controller.request.WxMiniPrepayReq;
import com.sc.nft.entity.vo.mall.MallWalletPayVO;
import com.sc.nft.entity.vo.mall.MallWxPayVO;
import com.sc.nft.entity.vo.mall.MiniMallOrderConfirmVO;
import com.sc.nft.enums.PayMethodEnum;
import com.sc.nft.enums.RedisKeyEnum;
import com.sc.nft.exception.GlobalException;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.feign.WxMiniPayClient;
import com.sc.nft.pool.UserPool;
import com.sc.nft.req.mallCart.CartListReq;
import com.sc.nft.req.wxPay.NotifyReq;
import com.sc.nft.service.MallPayOrderService;
import com.sc.nft.service.WxMiniPayService;
import com.sc.nft.sup.ErrorCode;
import com.sc.nft.sup.Result;
import com.sc.nft.vo.mallcart.MallCartListVO;
import com.sc.nft.vo.wxPay.PrepayWithRequestPaymentResponse;
import com.sc.nft.vo.wxPay.RefundNotification;
import com.sc.nft.vo.wxPay.Transaction;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author：LiGen
 * @Package：com.sc.nft.controller
 * @Project：sc_metago
 * @name：WxMiniMallPaymentController
 * @Date：2024/5/16 11:53
 * @Filename：WxMiniMallPaymentController
 */
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/pay/wxMiniMall")
@Api(tags = "小程序商城支付")
public class WxMiniMallPaymentController {

    private final WxMiniPayService wxMiniPayService;
    private final Redisson redisson;
    private final WxMiniPayClient wxMiniPayClient;
    private final MallPayOrderService mallPayOrderService;

    @ApiOperation(value = "微信小程序调起支付", notes = "微信小程序调起支付", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/prepay", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public Result<PrepayWithRequestPaymentResponse> mallWalletPay(@Valid @RequestBody WxMiniPrepayReq request, HttpServletRequest httpServletRequest) throws GlobalException {
        PrepayWithRequestPaymentResponse prepayWithRequestPaymentResponse = null;
        // 商城小程序订单用户锁
        RLock userLock = redisson.getLock(RedisKeyEnum.MALL_WX_ORDER_PAY_USER_LOCK.getKey() + UserPool.getWxMiniUser());
        List<RLock> locks = new ArrayList();
        try {
            boolean res = userLock.tryLock(3, 10, TimeUnit.SECONDS);
            if (res) {
                List<Long> missingLockOrderIdList = new ArrayList<>();
                List<Long> orderIdList = request.getOrderIds();
                for (Long orderId : orderIdList) {
                    // 订单锁
                    RLock lock = redisson.getLock(RedisKeyEnum.MALL_WX_ORDER_PAY_LOCK.getKey() + orderId);
                    boolean can = lock.tryLock(10, TimeUnit.SECONDS);
                    if (!can) {
                        missingLockOrderIdList.add(orderId);
                    } else {
                        locks.add(lock);
                    }
                }
                if (missingLockOrderIdList.size() > 0) {
                    // 只要有一个锁获取失败 就调起支付
                    throw new GlobalException("当前人数过多，请稍后再试!");
                }
                prepayWithRequestPaymentResponse = wxMiniPayService.wxMiniPrepay(orderIdList, UserPool.getWxMiniUser(), ServletUtil.getClientIP(httpServletRequest));
            } else {
                return Result.fail("当前人数过多，请稍后再试!");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            throw new GlobalRunTimeException("系统超时");
        } finally {
            userLock.unlock();
            // 批量释放锁
            locks.forEach(lock -> {
                lock.unlock();
                log.info("购物车创建订单释放锁:{}", lock);

            });
        }
        return Result.success(prepayWithRequestPaymentResponse);
    }

    @ApiOperation(value = "商户订单号查询订单状态", notes = "商户订单号查询订单状态", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/queryWxMiniPayByOutTradeNo", produces = MediaType.APPLICATION_JSON_VALUE)
    @UserLogin
    public Result<MallWxPayVO> mallWalletPay(@Valid @RequestBody IdRequest request) {
        return Result.success(wxMiniPayService.queryWxMiniPayByOutTradeNo(request.getId()));
    }

    @ApiOperation(value = "微信支付回调", notes = "微信支付回调", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/payNotify", produces = MediaType.APPLICATION_JSON_VALUE)
    @WithoutSign
    public ResponseEntity payNotify(HttpServletRequest request) throws Exception {
        log.info("微信支付回调开始======");
        //读取请求体的信息
        ServletInputStream inputStream = request.getInputStream();
        StringBuffer stringBuffer = new StringBuffer();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s;
        //读取回调请求体
        while ((s = bufferedReader.readLine()) != null) {
            stringBuffer.append(s);
        }
        String body = stringBuffer.toString();
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String signType = request.getHeader("Wechatpay-Signature-Type");
        String serialNo = request.getHeader("Wechatpay-Serial");
        String signature = request.getHeader("Wechatpay-Signature");
        log.info("微信支付回调:{}", body);
        log.info("timestamp:{}", timestamp);
        log.info("nonce:{}", nonce);
        log.info("signType:{}", signType);
        log.info("serialNo:{}", serialNo);
        log.info("signature:{}", signature);
        //验证签名
        Result<Transaction> transactionResult = wxMiniPayClient.payNotify(NotifyReq.build(body, timestamp, nonce, signType, serialNo, signature));
        if (!ErrorCode.SUCCESS.getCode().equals(transactionResult.getCode())) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
        Transaction transaction = transactionResult.getData();
        log.info("微信支付回调:{}", JSON.toJSONString(transaction));
        return wxMiniPayService.payNotify(transaction);
    }

    @ApiOperation(value = "微信退款回调", notes = "微信退款回调", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @PostMapping(value = "/refundPayNotify", produces = MediaType.APPLICATION_JSON_VALUE)
    @WithoutSign
    public ResponseEntity refundPayNotify(HttpServletRequest request) throws Exception {
        log.info("微信退款回调开始======");
        //读取请求体的信息
        ServletInputStream inputStream = request.getInputStream();
        StringBuffer stringBuffer = new StringBuffer();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s;
        //读取回调请求体
        while ((s = bufferedReader.readLine()) != null) {
            stringBuffer.append(s);
        }
        String body = stringBuffer.toString();
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String signType = request.getHeader("Wechatpay-Signature-Type");
        String serialNo = request.getHeader("Wechatpay-Serial");
        String signature = request.getHeader("Wechatpay-Signature");
        Result<RefundNotification> refundNotificationResult = wxMiniPayClient.refundPayNotify(NotifyReq.build(body, timestamp, nonce, signType, serialNo, signature));
        if (!ErrorCode.SUCCESS.getCode().equals(refundNotificationResult.getCode())) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
        RefundNotification refundNotification = refundNotificationResult.getData();
        log.info("微信退款回调:{}", JSON.toJSONString(refundNotification));
        try {
            mallPayOrderService.refundSuccess(refundNotification.getOutRefundNo(), DateUtil.format(DateUtil.parse(refundNotification.getSuccessTime()),"yyyyMMddHHmmss") , JSON.toJSONString(refundNotification), PayMethodEnum.WX_PAY);
        } catch (GlobalException e) {
            throw new RuntimeException(e);
        }
        return ResponseEntity.status(HttpStatus.OK).build();
    }
}
